﻿using Gargoyle.Roton;
using Gargoyle.ZZT;
using OpenTK;
using OpenTK.Input;
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Lyon
{
    /// <summary>
    /// Runs Lyon using a simple OpenGL renderer.
    /// </summary>
    class OpenGLTiledWindow : OpenGLWindow
    {
        private Color[] Palette;
        private const int NUM_GLYPHS = 256;
        private int[] Glyphs;

        public OpenGLTiledWindow() : base() { }
        public OpenGLTiledWindow(decimal Scale) : base(Scale) { }

        protected override void DoGLInit()
        {
            // load game palette
            Palette = Video.GetPalette();

            // generate the initial textures
            GL.Enable(EnableCap.Texture2D);
            byte[] fontData;
            int fontHeight;
            Glyphs = new int[NUM_GLYPHS];
            GL.GenTextures(NUM_GLYPHS, Glyphs);
            fontData = Video.GetFont();
            fontHeight = fontData.Length / NUM_GLYPHS;
            int texSize = 8 * fontHeight * 4;

            // generate textures for each font
            for(int i = 0; i < NUM_GLYPHS; i++)
            {
                // create a 32-bit RGBA texture representing each glyph
                byte[] textureData = new byte[texSize];
                byte value;
                byte row;
                int bytePosition = 0;
                IntPtr texPtr = Marshal.AllocHGlobal(texSize);

                for(int y = 0; y < fontHeight; y++)
                {
                    row = fontData[y + (fontHeight * i)];
                    for(int x = 0; x < 8; x++)
                    {
                        bytePosition = ((y * 8) + x) * 4;
                        value = ((row & (byte)0x80) != 0) ? (byte)0xFF : (byte)0x00;
                        textureData[bytePosition]
                            = textureData[bytePosition + 1]
                            = textureData[bytePosition + 2]
                            = textureData[bytePosition + 3] = value;
                        row <<= 1;
                    }
                }

                Marshal.Copy(textureData, 0, texPtr, texSize);
                GL.BindTexture(TextureTarget.Texture2D, Glyphs[i]);
                GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 8, fontHeight, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, texPtr);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
            }

        }

        protected override void DoRender(FrameEventArgs e)
        {
            AnsiChar ansiChar;
            VideoSection screen;
            byte chColor, chChar;
            screen = Video.GetSection(new Rectangle(0, 0, 80, 25));

            // draw background
            GL.Disable(EnableCap.Blend);
            GL.Disable(EnableCap.Texture2D);
            for(int y = 0; y < 25; y++)
            {
                for(int x = 0; x < 80; x++)
                {
                    chColor = screen.Chars[(y * (Common.WideMode ? 2 : 1) * GameWidth) + x].Color;
                    chColor >>= 4;  // push off foreground color
                    if(Palette[chColor] == Color.Black) continue;

                    if(VideoBlinkOn && chColor >= 0x08)
                        GL.Color4(Palette[chColor - 0x08]);
                    else
                        GL.Color4(Palette[chColor]);

                    GL.Begin(BeginMode.Quads);
                    if(!Common.WideMode)
                    {
                        GL.Vertex2(x, y);
                        GL.Vertex2(x + 1, y);
                        GL.Vertex2(x + 1, y + 1);
                        GL.Vertex2(x, y + 1);
                    }
                    else
                    {
                        GL.Vertex2(x * 2, y);
                        GL.Vertex2(x * 2 + 2, y);
                        GL.Vertex2(x * 2 + 2, y + 1);
                        GL.Vertex2(x * 2, y + 1);
                    }
                    GL.End();
                }
            }

            // overlay glyphs on top of backgrounds
            GL.Enable(EnableCap.Blend);
            GL.Enable(EnableCap.Texture2D);
            GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

            for(int y = 0; y < GameHeight; y++)
            {
                for(int x = 0; x < GameWidth; x++)
                {
                    ansiChar = screen.Chars[(y * (Common.WideMode ? 2 : 1) * GameWidth) + x];
                    chColor = ansiChar.Color;
                    chChar = ansiChar.Character;

                    // check to see if the character should blink
                    if(VideoBlinkOn && ((chColor >> 4) >= 0x08))
                        GL.Color4(GetBlinkColor(chColor));
                    else
                        GL.Color4(Palette[chColor & 0x0F]);

                    GL.BindTexture(TextureTarget.Texture2D, Glyphs[chChar]);

                    GL.Begin(BeginMode.Quads);
                    if(!Common.WideMode)
                    {
                        GL.TexCoord2(0.0f, 0.0f); GL.Vertex2(x, y);
                        GL.TexCoord2(1.0f, 0.0f); GL.Vertex2(x + 1, y);
                        GL.TexCoord2(1.0f, 1.0f); GL.Vertex2(x + 1, y + 1);
                        GL.TexCoord2(0.0f, 1.0f); GL.Vertex2(x, y + 1);
                    }
                    else
                    {
                        GL.TexCoord2(0.0f, 0.0f); GL.Vertex2(x * 2, y);
                        GL.TexCoord2(1.0f, 0.0f); GL.Vertex2(x * 2 + 2, y);
                        GL.TexCoord2(1.0f, 1.0f); GL.Vertex2(x * 2 + 2, y + 1);
                        GL.TexCoord2(0.0f, 1.0f); GL.Vertex2(x * 2, y + 1);
                    }
                    GL.End();
                }
            }
        }

        private Color GetBlinkColor(byte color)
        {
            if(VideoBlink)  // return background color
                return Palette[(color >> 4) - 0x08];
            else            // return foreground color
                return Palette[color & 0x0F];
        }

        protected override void DoUnload()
        {
            if(Initialized)
                GL.DeleteTextures(NUM_GLYPHS, Glyphs);
        }
    }
}
